package lib.api.opensubtitles;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern;

import lib.api.XmlRPCAPI;
import lib.api.opensubtitles.model.OpenSubtitlesMovieDescriptor;
import lib.api.opensubtitles.model.OpenSubtitlesSubtitleDescriptor;
import lib.logging.Logger;
import redstone.xmlrpc.XmlRpcFault;
import util.NamedMatcher;
import util.NamedPattern;

public class JOpenSubtitlesApi extends XmlRPCAPI{

    public JOpenSubtitlesApi(String useragent) throws MalformedURLException, XmlRpcFault {
        super(useragent, "http://api.opensubtitles.org/xml-rpc");
        if (!isLoggedOn())
                loginAnonymous();
    }

    public void loginAnonymous() throws XmlRpcFault, MalformedURLException {
        login("", "");
    }

    public void login(String username, String password) throws XmlRpcFault,
            MalformedURLException {
        login(username, password, "en");
    }

    public synchronized void login(String username, String password,
                                   String language) throws MalformedURLException, XmlRpcFault {
        Map<?, ?> response = invoke("LogIn", username, password, language, getUserAgent());

//		String xml = generateXmlRpc("LogIn", new String []{username, password, language, useragent});
//
//		Map<?, ?> response = invokeXML(xml);
        // set session token
        setToken(response.get("token").toString());
    }

    public synchronized void logout() throws MalformedURLException {
        try {
            invoke("LogOut", getToken());
        } catch (XmlRpcFault e) {
            // anonymous users will always get an 401 Unauthorized when trying
            // to logout,
            // so we ignore the status of the logout response
        } finally {
            this.setToken(null);
        }
    }

    @SuppressWarnings("unchecked")
    public Map<String, String> getServerInfo() throws XmlRpcFault,
            MalformedURLException {
        return (Map<String, String>) invoke("ServerInfo", getToken());
    }

    @SuppressWarnings("unchecked")
    public List<OpenSubtitlesMovieDescriptor> searchMoviesOnIMDB(String title)
            throws XmlRpcFault, MalformedURLException {
        List<OpenSubtitlesMovieDescriptor> movies = new ArrayList<OpenSubtitlesMovieDescriptor>();
        Map<?, ?> response = invoke("SearchMoviesOnIMDB", getToken(), title);

        List<Map<String, String>> movieData = (List<Map<String, String>>) response
                .get("data");

        NamedPattern np = NamedPattern.compile("(?<moviename>[\\w\\s:&().,_-]+)[\\.|\\[|\\(| ]{1}(?<year>19\\d{2}|20\\d{2})", Pattern.CASE_INSENSITIVE);
        NamedMatcher nm;

        for (Map<String, String> movie : movieData) {
            // get non-aka title (aka titles were separated by Â, and then aka
            // later on)
        	
            Scanner titleScanner = new Scanner(movie.get("title"))
                    .useDelimiter("(\u00C2)|(\\s+aka\\s+)");

            nm = np.matcher(titleScanner.next().trim());

            int imdbid = 0;
            int year = 0;

            if (nm.find()) {
                try {
                    imdbid = Integer.parseInt(movie.get("id"));
                    year = Integer.parseInt(nm.group("year"));
                } catch (Exception e) {
                    Logger.instance.error(e.getMessage());
                }

                movies.add(new OpenSubtitlesMovieDescriptor(nm.group("moviename"), year, imdbid));
            }
            
            titleScanner.close();
        }

        return movies;
    }

    public OpenSubtitlesMovieDescriptor getIMDBMovieDetails(int imdbid)
            throws XmlRpcFault, MalformedURLException {
        Map<?, ?> response = invoke("GetIMDBMovieDetails", getToken(), imdbid);

        try {
            Map<String, String> data = (Map<String, String>) response
                    .get("data");

            String name = data.get("title");
            int year = Integer.parseInt(data.get("year"));

            return new OpenSubtitlesMovieDescriptor(name, year, imdbid);
        } catch (RuntimeException e) {
            // ignore, invalid response
        }

        return null;
    }

    public synchronized boolean isLoggedOn() {
        return this.getToken() != null;
    }

    public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(String moviehash, String moviebytesize, String... languages) throws Exception {
        Map<String, Object> queryList = new HashMap<String, Object>();
        queryList.put("moviehash", moviehash);
        queryList.put("moviebytesize", moviebytesize);
        return searchSubtitles(queryList, languages);
    }

    public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, String... languages) throws Exception {
        Map<String, Object> queryList = new HashMap<String, Object>();
        queryList.put("imdbid", imdbid);
        return searchSubtitles(queryList, languages);
    }

    public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(String query, String... languages) throws Exception {
        Map<String, Object> queryList = new HashMap<String, Object>();
        queryList.put("query", query);
        return searchSubtitles(queryList, languages);
    }
    
    public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(String query, int season, List<Integer> episode, String... languages) throws Exception {
        Map<String, Object> queryList = new HashMap<String, Object>();
        queryList.put("query", query);
        queryList.put("season", season);
        queryList.put("episode", episode.get(0));
        return searchSubtitles(queryList, languages);
    }

    @SuppressWarnings("unchecked")
    public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(Map<String, Object> queryList, String... languages)
            throws Exception {
        List<OpenSubtitlesSubtitleDescriptor> subtitles = new ArrayList<OpenSubtitlesSubtitleDescriptor>();

        StringBuilder result = new StringBuilder();
        String separator = ",";
        if (languages.length > 0) {
            result.append(OS_LANGS.get(languages[0]));
            for (int i = 1; i < languages.length; i++) {
                result.append(separator);
                result.append(OS_LANGS.get(languages[i]));
            }
        }

        queryList.put("sublanguageid", result.toString());

        String xml = generateXmlRpc("SearchSubtitles", queryList);

        Map<?, ?> response = invokeXML(xml);
        //Map<?, ?> response = invoke("SearchSubtitles", getToken(), queryList);
        try {
            List<Map<String, String>> subtitleData = (List<Map<String, String>>) response.get("data");

            for (Map<String, String> subtitle : subtitleData) {
                subtitles.add(parseOSSubtitle(subtitle));
            }
        } catch (Exception e) {
            //do nothing
        }
        return subtitles;
    }

    private OpenSubtitlesSubtitleDescriptor parseOSSubtitle(
            Map<String, String> subtitle) {
        OpenSubtitlesSubtitleDescriptor oss = new OpenSubtitlesSubtitleDescriptor();
        oss.setUserNickName(subtitle.get("UserNickName"));
        oss.setSubFormat(subtitle.get("SubFormat"));
        oss.setIDSubtitle(Integer.parseInt(subtitle.get("IDSubtitle")));
        oss.setIDMovie(Integer.parseInt(subtitle.get("IDMovie")));
        oss.setSubBad(subtitle.get("SubBad"));
        oss.setUserID(Integer.parseInt(subtitle.get("UserID")));
        oss.setZipDownloadLink(subtitle.get("ZipDownloadLink"));
        oss.setSubSize(Long.parseLong(subtitle.get("SubSize")));
        oss.setSubFileName(subtitle.get("SubFileName"));
        oss.setSubDownloadLink(subtitle.get("SubDownloadLink"));
        oss.setUserRank(subtitle.get("UserRank"));
        oss.setSubActualCD(subtitle.get("SubActualCD"));
        oss.setMovieImdbRating(subtitle.get("MovieImdbRating"));
        oss.setSubAuthorComment(subtitle.get("SubAuthorComment"));
        oss.setSubRating(subtitle.get("SubRating"));
        oss.setSubtitlesLink(subtitle.get("SubtitlesLink"));
        oss.setSubHearingImpaired(subtitle.get("SubHearingImpaired"));
        oss.setSubHash(subtitle.get("SubHash"));
        oss.setIDSubMovieFile(Integer.parseInt(subtitle.get("IDSubMovieFile")));
        oss.setISO639(subtitle.get("ISO639"));
        oss.setSubDownloadsCnt(Integer
                .parseInt(subtitle.get("SubDownloadsCnt")));
        oss.setMovieHash(subtitle.get("MovieHash"));
        oss.setSubSumCD(Integer.parseInt(subtitle.get("SubSumCD")));
        oss.setSubComments(subtitle.get("SubComments"));
        oss.setMovieByteSize(Long.parseLong(subtitle.get("MovieByteSize")));
        oss.setLanguageName(subtitle.get("LanguageName"));
        oss.setMovieYear(Integer.parseInt(subtitle.get("MovieYear")));
        oss.setSubLanguageID(subtitle.get("SubLanguageID"));
        oss.setMovieReleaseName(subtitle.get("MovieReleaseName"));
        oss.setMovieTimeMS(subtitle.get("MovieTimeMS"));
        oss.setMatchedBy(subtitle.get("MatchedBy"));
        oss.setMovieName(subtitle.get("MovieName"));
        oss.setSubAddDate(subtitle.get("SubAddDate"));
        oss.setIDMovieImdb(Integer.parseInt(subtitle.get("IDMovieImdb")));
        oss.setMovieNameEng(subtitle.get("MovieNameEng"));
        oss.setIDSubtitle(Integer.parseInt(subtitle.get("IDSubtitleFile")));
        return oss;
    }
    
    private static final Map<String, String> OS_LANGS = Collections
            .unmodifiableMap(new HashMap<String, String>() {
                /**
                 *
                 */
                private static final long serialVersionUID = 1683941675464726802L;

                {
                    put("en", "eng");
                    put("fr", "fre");
                    put("hu", "hun");
                    put("cs", "cze");
                    put("pl", "pol");
                    put("sk", "slo");
                    put("pt", "por");
                    put("pt-br", "pob");
                    put("es", "spa");
                    put("el", "ell");
                    put("ar", "ara");
                    put("sq", "alb");
                    put("hy", "arm");
                    put("ay", "ass");
                    put("bs", "bos");
                    put("bg", "bul");
                    put("ca", "cat");
                    put("zh", "chi");
                    put("hr", "hrv");
                    put("da", "dan");
                    put("nl", "dut");
                    put("eo", "epo");
                    put("et", "est");
                    put("fi", "fin");
                    put("gl", "glg");
                    put("ka", "geo");
                    put("de", "ger");
                    put("he", "heb");
                    put("hi", "hin");
                    put("is", "ice");
                    put("id", "ind");
                    put("it", "ita");
                    put("ja", "jpn");
                    put("kk", "kaz");
                    put("ko", "kor");
                    put("lv", "lav");
                    put("lt", "lit");
                    put("lb", "ltz");
                    put("mk", "mac");
                    put("ms", "may");
                    put("no", "nor");
                    put("oc", "oci");
                    put("fa", "per");
                    put("ro", "rum");
                    put("ru", "rus");
                    put("sr", "scc");
                    put("sl", "slv");
                    put("sv", "swe");
                    put("th", "tha");
                    put("tr", "tur");
                    put("uk", "ukr");
                    put("vi", "vie");

                }
            });

}
